﻿#pragma warning disable 0649

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.ComponentModel;
using KinectDwa.InputDevices;

namespace KinectDwa.Parsers.Helpers
{
    public static class MouseEmulator
    {
        public const int MouseEventMove = 0x01;
        public const int MouseEventLeftDown = 0x02;
        public const int MouseEventLeftUp = 0x04;
        public const int MouseEventRightDown = 0x08;
        public const int MouseEventRightUp = 0x10;
        public const int MouseEventAbsolute = 0x8000;

        // Mouse status
        private static bool leftDown = false;
        private static bool rightDown = false;
        private static short leftDownCounter = 0;
        private static short rightDownCounter = 0;
        private static short clickCoolDown = 0;

        internal struct MouseInput
        {
            public int X;
            public int Y;
            public uint MouseData;
            public uint Flags;
            public uint Time;
            public IntPtr ExtraInfo;
        }

        internal struct Input
        {
            public int Type;
            public MouseInput MouseInput;
        }

        [DllImport("user32.dll", SetLastError = true)]
        private static extern uint SendInput(uint numInputs, Input[] inputs, int size);

        private static List<int> previousX = new List<int>();
        private static List<int> previousY = new List<int>();
        public static void MouseEvent(int rawPositionX, int rawPositionY, bool leftPressed, bool rightPressed)
        {
            // Do nothing if we have no mouse
            if (MouseInputControl.disableMouse)
                return;

            // Soften the mouse cursor
            previousX.Add(rawPositionX);
            previousY.Add(rawPositionY);

            if (previousX.Count() > MouseInputControl.mouseSofteningMemoryCount)
            {
                previousX.RemoveAt(0);
                previousY.RemoveAt(0);
            }

            int positionX = 0;
            int positionY = 0;

            foreach (int x in previousX)
            {
                positionX += x;
            }

            foreach (int y in previousY)
            {
                positionY += y;
            }

            positionX /= previousX.Count;
            positionY /= previousY.Count;
            
            // Cancel any gesture inputs if clicks are disabled
            if (MouseInputControl.disableClicks)
            {
                leftPressed = false;
                rightPressed = false;
            }

            if (leftDownCounter > 0)
            {
                --leftDownCounter;
            }

            if (rightDownCounter > 0)
            {
                --rightDownCounter;
            }

            if (clickCoolDown > 0)
            {
                --clickCoolDown;
            }

            Input[] i = new Input[2];

            i[0] = new Input();
            i[0].Type = 0;
            i[0].MouseInput.X = (positionX * 65535) / (int)System.Windows.SystemParameters.PrimaryScreenWidth;
            i[0].MouseInput.Y = (positionY * 65535) / (int)System.Windows.SystemParameters.PrimaryScreenHeight;
            i[0].MouseInput.Flags = MouseEventAbsolute | MouseEventMove;

            if (leftDown)
            {
                if (!leftPressed)
                {
                    if (leftDownCounter <= 0)
                    {
                        i[1] = new Input();
                        i[1].Type = 0;
                        i[1].MouseInput.Flags = MouseEventLeftUp;
                        leftDown = false;
                        clickCoolDown = MouseInputControl.clickCoolDown;
                    }
                }
                else
                {
                    // Refresh left down counter when left is down
                    leftDownCounter = MouseInputControl.clickPersistency;
                }
            }
            else if (rightDown)
            {
                if (!rightPressed)
                {
                    if (rightDownCounter <= 0)
                    {
                        i[1] = new Input();
                        i[1].Type = 0;
                        i[1].MouseInput.Flags = MouseEventRightUp;
                        rightDown = false;
                        clickCoolDown = MouseInputControl.clickCoolDown;
                    }
                }
                else
                {
                    // Refresh right down counter when left is down
                    rightDownCounter = MouseInputControl.clickPersistency;
                }
            }
            else
            {
                if (clickCoolDown <= 0)
                {
                    if (leftPressed)
                    {
                        i[1] = new Input();
                        i[1].Type = 0;
                        i[1].MouseInput.Flags = MouseEventLeftDown;
                        leftDown = true;
                        leftDownCounter = MouseInputControl.clickPersistency;
                    }
                    else if (rightPressed)
                    {
                        i[1] = new Input();
                        i[1].Type = 0;
                        i[1].MouseInput.Flags = MouseEventRightDown;
                        rightDown = true;
                        rightDownCounter = MouseInputControl.clickPersistency;
                    }
                }
            }

            uint result = SendInput(2, i, Marshal.SizeOf(i[0]));
            if (result == 0)
                throw new Win32Exception(Marshal.GetLastWin32Error());
        }
    }
}
